#include "av.h"

static
void lua_format_context_close_input_file(void * data) {
	av_close_input_file((AVFormatContext *)data);
}

extern
int lua_format_context_open(lua_State *L) {
	const char *path = luaL_checkstring (L, 1);
	AVFormatContext *ctx;
	lua_av_object * o;
	int err;

	if ((err = av_open_input_file(&ctx, path, NULL, 0, NULL)) != 0) {
		luaL_error (L, "cannot open %s (%d)", path, err);
		return 0;
	}
	if ((err = av_find_stream_info(ctx)) < 0) {
		luaL_error (L, "unable to find stream for %s (%d)", path, err);
		return 0;
	}
	lua_push_format_context(L, ctx);
	o = (lua_av_object *) lua_touserdata(L, -1);
	o->destroy = lua_format_context_close_input_file;
	return 1;
}

extern
int lua_format_context_new(lua_State *L) {
	const char *filename = luaL_checkstring (L, 1);
	AVFormatContext *ctx;
	ctx = avformat_alloc_context();
	if(!ctx) {
		luaL_error (L, "cannot allocate a format context");
		return 0;
	}
	snprintf(ctx->filename, sizeof(ctx->filename), filename);
	lua_push_format_context(L, ctx);
	return 1;
}

static
int av_format_context_get_stream(lua_State * L) {
	AVFormatContext *ctx;
	lua_av_object * o;
	int stream;

	lua_check_format_context(L, 1, &ctx);
	stream = luaL_checkinteger(L, 2) - 1;
	if (stream < 0 || stream >= ctx->nb_streams) {
		luaL_error (L, "invalid stream index");
		return 0;
	}
	o = lua_push_stream(L, ctx->streams[stream]);
	o->destroy = 0;

	return 1;
}

static
int av_format_context_new_stream(lua_State * L) {
	AVFormatContext *ctx;
	int id;
	AVStream * s;
	lua_av_object * o;

	lua_check_format_context(L, 1, &ctx);
	id = luaL_checkinteger(L, 2) - 1;
	s = av_new_stream(ctx, id);
	o = lua_push_stream(L, s);
	o->destroy = 0;
	return 1;
}

static
int av_format_context_write_frame(lua_State * L) {
	AVFormatContext *ctx;
	AVPacket *pkt;
	int error;

	lua_check_format_context(L, 1, &ctx);
	lua_check_packet(L, 2, &pkt);

	error = av_write_frame(ctx, pkt);
	if (error < 0) {
		luaL_error(L, "error writing frame (%d)", error);
		return 0;
	}
	lua_pushboolean(L, error);
	return 1;
}

static
int av_format_context_write_header(lua_State * L) {
	AVFormatContext *ctx;
	int error;

	lua_check_format_context(L, 1, &ctx);

	error = av_write_header(ctx);
	if (error < 0) {
		luaL_error(L, "error writing header (%d)", error);
	}
	return 0;
}

static
int av_format_context_write_trailer(lua_State * L) {
	AVFormatContext *ctx;
	int error;

	lua_check_format_context(L, 1, &ctx);

	error = av_write_trailer(ctx);
	if (error < 0) {
		luaL_error(L, "error writing trailer (%d)", error);
	}
	return 0;
}

static
int av_format_context_read_frame(lua_State * L) {
	AVFormatContext *ctx;
	AVPacket *pkt;
	int error;

	lua_check_format_context(L, 1, &ctx);
	lua_check_packet(L, 2, &pkt);

	error = av_read_frame(ctx, pkt);
	lua_pushboolean(L, error == 0);
	return 1;
}

static
const struct luaL_reg av_format_context_reg[] = {
	{"get_stream", av_format_context_get_stream},
	{"new_stream", av_format_context_new_stream},
	{"write_header", av_format_context_write_header},
	{"write_frame", av_format_context_write_frame},
	{"write_trailer", av_format_context_write_trailer},
	{"read_frame", av_format_context_read_frame},
	{NULL, NULL},
};

DEFINE_TYPE_STRUCT(format_context, AVFormatContext,
	LUA_OBJECT_FORMAT_CONTEXT_ID, av_free, av_format_context_reg)

DEFINE_INDEX_HEADER(format_context, AVFormatContext)
	HANDLE_INDEX_ENTRY(nb_streams, lua_pushinteger);
	HANDLE_INDEX_ENTRY(oformat, lua_push_output_format);
	HANDLE_INDEX_ENTRY(filename, lua_pushstring);
DEFINE_INDEX_TRAILER()

DEFINE_NEWINDEX_HEADER(format_context, AVFormatContext)
	HANDLE_NEWINDEX_OBJECT(oformat, output_format);
DEFINE_NEWINDEX_TRAILER()

DEFINE_OBJECT_FUNCTIONS(format_context, AVFormatContext)
